13-4 洏MATLABおwid

若要使用 MATLAB 來對資料庫進行查詢或修改,其基本流程可以分為下列幾個步驟:

  1. 設定 DSN,讓 MATLAB 能經由 DSN 及 ODBC 來操控資料庫。(此部分請見上一節的說明)
  2. 使用 logintimeout 指令來設定當 MATLAB 在進行資料庫連結時,最長的嘗試連結時間。例如,我們可用 logintimeout(5) 來設定此時間為 5 秒。在嘗試連結資料庫時,一旦超過此時間,MATLAB 將產生錯誤訊息而不再進行連結。
  3. 使用 database 指令來連結至資料庫,其格式如下:
  4. conn = database(dsn, loginName, password)
  5. 其中 dsn 是在步驟 1 所設定的 DSN,loginName 和 password則是存取此資料庫的帳號和密碼。若此資料庫不需帳號和密碼,則這兩個參數都可以設定成空字串。所傳回的變數 conn 則是代表資料庫連結的物件。
  6. 使用 ping 指令來檢查資料庫連結狀態,例如 ping(conn)。這步驟純粹是為了檢查資料庫連結狀態,在一般程式碼中,可以省略。
  7. 使用 exec 指令來執行 SQL 命令,並傳回 cursor 物件,以邊開始抓取資料。其格式如下:
  8. cursor = exec(conn, sqlCommand)
  9. 其中 conn 是在步驟 3 所設定的 資料庫連結物件,sqlCommand 則是欲執行的 SQL 命令,所傳回的 cursor 物件,代表此次執行 SQL命令的管道或溝通方式,我們可以根據此管道來取得所有的資料。
  10. 可以使用 setdbprefs 指令來設定回傳資料的格式,一般是異質陣列(預設值)或是結構陣列,詳見後面的範例。
  11. 使用 fetch 指令來抓取資料,例如 cursor = fetch(cursor, 10) 會抓取10筆資料,並將資料存放在 cursor 物件中的 Data 欄位。
  12. 將 cursor 物件的資料傳至 MATLAB 變數,例如:songData = cursor.Data。
  13. 可以反覆使用 cursor 物件來取得下達 SQL 命令所得到的所有資料。
  14. 最後可以使用 close 指令來關閉 cursor 物件及資料庫連結。

這幾個步驟,看起來有一些繁複,但在實際操作時,並非如想像中複雜。我們直接看一個範例,會比較容易進入狀況。在下面這個範例中,我們使用前述的步驟,抓出song 資料表(位於 song01.mdb)內的資料。當然,在嘗試此範例時,你必須先使用前一節的方法來設定 DSN,將 dsnSong01 指向 song01.mdb 的本機位置。範例如下。

Example 1: 13-與資料庫的整合/getData01.mdsn = 'dsnSong01'; % 設定資料來源名稱(指到 song01.mdb) logintimeout(5); % 設定嘗試連結資料庫的時間 conn = database(dsn, '', ''); % 連結資料庫 sql = 'select * from song'; % 執行設定 SQL 命令 cursor = exec(conn, sql); % 執行 SQL 命令,並傳回 cursor 物件 cursor = fetch(cursor, 8); % 經由 cursor 物件,抓取 8 筆資料 songData = cursor.data % 將資料傳至 MATLAB 變數 songData close(cursor); % 結束 cursor 物件 close(conn); % 結束資料庫連結songData = [ 1] '用心良苦' '張宇' [1993] [ 2] '聽海' '張惠妹' [1998] [ 4] '牽手' '張惠妹' [2001] [ 6] '最熟悉的陌生人' '蕭亞軒' [2000] [ 8] '戀人未滿' 'S.H.E' [2002] [ 9] 'I.O.I.O.' 'S.H.E' [2002] [11] '每次都想呼喊你的名字' '永邦' [2002] [12] '最熟悉的陌生人' '蕭亞軒' [2000]

在上述範例中,cursor 物件的作用,類似讀取檔案時所用的檔案指標,讓我們在下達一次SQL命令後,可以記錄資料分次讀出的情況。例如,cursor = fetch(cursor, 8) 的作用是經由 cursor 物件抓取 8 筆資料,如果再一次下達 cursor = fetch(cursor, 2),就會抓取下兩筆資料,並記錄於 cursor 物件。如果資料量不大,我們也可以直接下達 cursor = fetch(cursor) ,此時會將所有的資料全部抓回,並紀錄在 cursor 物件。

在關閉 cursor 及關閉 conn 之前,你也可以直接在 MATLAB 輸入 conn 或是 cursor,就可以看到這兩個物件到底藏了些什麼資訊。

另外我們可以注意到,傳回的結果是放在一個異質陣列,並依照欄位資料型態的不同而有不同的資料型態,例如數值或字串等。但是這些資料並不包含欄位名稱,若要抓取欄位名稱,可以使用 columnnames 指令,其他類似的指令還有 rows(資料筆數)、cols(欄位個數)、width(某個欄位的寬度)、attr(某個欄位的所有資訊),範例如下。

Example 2: 13-與資料庫的整合/getDataAttr01.mdsn = 'dsnSong01'; % 設定資料來源名稱(指到 song01.mdb) logintimeout(5); % 設定嘗試連結資料庫的時間 conn = database(dsn, '', ''); % 連結資料庫 sql = 'select * from song'; % 設定 SQL 命令 cursor = exec(conn, sql); % 執行 SQL 命令,並傳回 cursor 物件 cursor = fetch(cursor, 10); % 經由 cursor 物件,抓取 10 筆資料 fprintf('資料筆數 = %d\n', rows(cursor)); % 顯示資料筆數 fprintf('欄位個數 = %d\n', cols(cursor)); % 顯示欄位個數 fprintf('欄位名稱 = %s\n', columnnames(cursor));% 顯示資料庫欄位名稱 fprintf('欄位寬度 = %d\n', width(cursor, 3)); % 顯示第三個欄位的寬度 attributes = attr(cursor, 3) % 顯示第三個欄位的所有資訊 close(cursor); % 結束 cursor 物件 close(conn); % 結束資料庫連結資料筆數 = 10 欄位個數 = 4 欄位名稱 = '序號','歌曲名稱','主唱者','年份' 欄位寬度 = 255 attributes = fieldName: '主唱者' typeName: 'VARCHAR' typeValue: 12 columnWidth: 255 precision: [] scale: [] currency: 'false' readOnly: 'false' nullable: 'true' Message: []

在上述範例中,cursor 物件所傳回的資料是異質陣列,我們可以使用 setdbprefs 指令,來將傳回的資料改成結構陣列,範例如下。(但在執行此範例之前,你必須先手動設定DSN,亦即將 dsnScore01 指向此範例所用的資料庫 score01.mdb。)

Example 3: 13-與資料庫的整合/getData02.mdsn = 'dsnScore01'; % 設定資料來源名稱(指到 score01.mdb) logintimeout(5); % 設定嘗試連結資料庫的時間 conn = database(dsn, '', ''); % 連結資料庫 sql = 'select * from score'; % 執行設定 SQL 命令 cursor = exec(conn, sql); % 執行 SQL 命令,並傳回 cursor 物件 setdbprefs('DataReturnFormat', 'structure'); % 設定 cursor 傳回資料格式是結構陣列 cursor = fetch(cursor, 10); % 經由 cursor 物件,抓取 10 筆資料 score = cursor.Data % 經資料設定至 score 變數 close(cursor); % 結束 cursor 物件 close(conn); % 結束資料庫連結 setdbprefs('DataReturnFormat', 'cellarray'); % 改回預設的資料格式score = studentID: {10x1 cell} studentName: {10x1 cell} homework1: [10x1 double] homework2: [10x1 double] homework3: [10x1 double] midterm: [10x1 double] final: [10x1 double] overall: [10x1 double]

在上述範例中,傳回的變數 score 是一個結構變數,包含 8 個欄位,每一個欄位值則都是一個異質陣列,包含原資料表 song 的欄位值。

Hint
但是必須注意的是:MATLAB結構陣列的欄位名稱只能使用英文,而不能用中文,因此在使用此種方式來轉換前,必須確認資料庫的欄位名稱必須是英文,否則就會發生錯誤。(例如,如果使用 song01.mdb 的song資料表來進行類似的轉換,就會發生錯誤,因為song資料表的欄位名稱都是中文。)

但是上述的安排方式,不見得是符合我們需要。我們是否能把傳回的資料轉換成一個結構陣列,每個元素就是一筆資料,並把資料庫的欄位當成是每個元素的欄位呢?答案是肯定的,我們所要用到的指令是 cell2struct,來將異質陣列轉換成結構陣列,範例如下。

Example 4: 13-與資料庫的整合/db2struct01.mDSN = 'dsnScore01'; % 設定資料來源名稱(指到 score01.mdb) logintimeout(5); % 設定嘗試連結資料庫的時間 conn = database(DSN, '', ''); % 連結資料庫 sql = 'select * from score order by studentID'; % 設定 SQL 命令 cursor = exec(conn, sql); % 執行 SQL 命令,並傳回 cursor 物件 cursor = fetch(cursor); % 經由 cursor 物件,抓取全部資料 score = cursor.data % 將資料傳至 MATLAB 變數 scoregData temp = columnnames(cursor); % 顯示資料庫欄位名稱 eval(['fieldNames = {', temp, '}'';']); % 將欄位名稱指定到 fieldNames 變數 score2 = cell2struct(score, fieldNames, 2) % 將異質陣列 score 轉成結構陣列 score2 close(cursor); % 結束 cursor 物件 close(conn); % 結束資料庫連結score = '8802142' '許肇凌' [86] [96] [92] [90] [78] [0] '882545' '王儀蓁' [86] [96] [92] [99] [77] [0] '882548' '蘇金龍' [89] [94] [90] [99] [78] [0] '898316' '李俊仁' [87] [95] [94] [99] [88] [0] '914307' '謝明峰' [89] [93] [93] [90] [77] [0] '914340' '傅思為' [82] [93] [87] [77] [80] [0] '914358' '徐茂原' [87] [93] [87] [90] [80] [0] '914370' '陳晴' [88] [93] [93] [95] [79] [0] '916310' '薛博謙' [83] [93] [93] [99] [80] [0] '916701' '羅瑞麟' [90] [94] [90] [90] [77] [0] '916716' '鍾綸' [88] [94] [87] [99] [77] [0] '916717' '林青慧' [90] [97] [92] [99] [80] [0] score2 = 12x1 struct array with fields: studentID studentName homework1 homework2 homework3 midterm final overall

由上述範例可以看出,score是一個異質陣列,但是使用cell2struct來進行轉換後,score2已經變成是一個結構陣列,其中每一個元素就是一筆資料,而且每一個元素的欄位名稱都和資料庫的欄位名稱一致。

如果資料量很大,那麼若要直接在 MATLAB 視窗內檢視,並不是很方便,可能的解決之道有兩種:

  1. 使用MATLAB的「陣列編輯器」(Array Editor)來檢視。例如執行過上述範例後,可以直接在MATLAB輸入 open score 或 open score2,就可以直接檢視陣列內容,也可以修改。
  2. 也可以將資料寫到網頁,以便上下捲動觀看。

下面這個範例,我們使用了一個筆者開發的函數 struct2html.m,可以將結構陣列的內容呈現至網頁,以便觀看,範例如下:

Example 5: 13-與資料庫的整合/db2struct02.mDSN = 'dsnScore01'; % 設定資料來源名稱(指到 score01.mdb) logintimeout(5); % 設定嘗試連結資料庫的時間 conn = database(DSN, '', ''); % 連結資料庫 sql = 'select * from score order by studentID'; % 設定 SQL 命令 cursor = exec(conn, sql); % 執行 SQL 命令,並傳回 cursor 物件 cursor = fetch(cursor); % 經由 cursor 物件,抓取全部資料 score = cursor.data; % 將資料傳至 MATLAB 變數 score temp = columnnames(cursor); % 顯示資料庫欄位名稱 eval(['fieldNames = {', temp, '}'';']); % 將欄位名稱指定到 fieldNames 變數 score2 = cell2struct(score, fieldNames, 2); % 將異質陣列 score 轉成結構陣列 score2 close(cursor); % 結束 cursor 物件 close(conn); % 結束資料庫連結 struct2html(score2); % 顯示結構陣列 score2 於瀏覽器

所產生的網頁瀏覽器如下:

以上說明都是針對資料庫的查詢,下一節將說明如何對資料庫進行修改。


MATLAB程式設計:進階篇